/*
 *
 * Copyright (c) 2022 The Raptor Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "raptor-lite/impl/engine.h"
#include "raptor-lite/impl/property.h"
#include "raptor-lite/impl/config.h"
#include "raptor-lite/impl/handler.h"

#include "src/surface/acceptor.h"
#include "src/surface/connector.h"
#include "src/surface/container.h"

#include "src/common/connection_id.h"
#include "src/common/common.h"

#ifdef _WIN32
#include "src/windows/iocp_thread.h"
#endif

namespace raptor {

class EngineImpl {

public:
    EngineImpl();
    ~EngineImpl();

#ifdef _WIN32
    raptor_error Init();
    void Start();
#endif

    void OptionBoundaryAdjustment(struct Option *option);

    raptor_error CreateAcceptor(const Property &p, Acceptor **out);
    void DestroyAcceptor(Acceptor *);

    raptor_error CreateConnector(const Property &p, Connector **out);
    void DestroyConnector(Connector *);

    raptor_error CreateContainer(const Property &p, Container **out);
    void DestroyContainer(Container *);

private:
#ifdef _WIN32
    PollingThread *impl_;
#else
    intptr_t impl_;
#endif
};

/*
 * Property:
 *   1. kAcceptorHandler (required)
 */
raptor_error EngineImpl::CreateAcceptor(const Property &p, Acceptor **out) {
    AcceptorHandler *handler =
        reinterpret_cast<AcceptorHandler *>(p.GetValue<intptr_t>(config::kAcceptorHandler));

    *out = nullptr;

    if (!handler) {
        return RAPTOR_ERROR_FROM_STATIC_STRING("Missing AcceptorHandler");
    }

    std::unique_ptr<AcceptorAdaptor> adaptor(new AcceptorAdaptor(handler));
#ifdef _WIN32
    raptor_error err = adaptor->Init(impl_);
#else
    raptor_error err = adaptor->Init();
#endif
    if (err == RAPTOR_ERROR_NONE) {
        *out = adaptor.release();
    }
    return err;
}

void EngineImpl::DestroyAcceptor(Acceptor *a) {
    if (a) {
        a->Shutdown();
        delete a;
    }
}

/*
 * Property:
 *   1. kConnectorHandler (required)
 *   2. kTcpUserTimeoutMs (optional, default:0)
 */
raptor_error EngineImpl::CreateConnector(const Property &p, Connector **out) {
    ConnectorHandler *handler =
        reinterpret_cast<ConnectorHandler *>(p.GetValue<intptr_t>(config::kConnectorHandler));

    int TcpUserTimeoutMs = p.GetValue(config::kTcpUserTimeoutMs, 0);

    *out = nullptr;

    if (!handler) {
        return RAPTOR_ERROR_FROM_STATIC_STRING("Missing ConnectorHandler");
    }

    std::unique_ptr<ConnectorAdaptor> adaptor(new ConnectorAdaptor(handler));

#ifdef _WIN32
    raptor_error err = adaptor->Init(impl_, TcpUserTimeoutMs);
#else
    raptor_error err = adaptor->Init(TcpUserTimeoutMs);
#endif

    if (err == RAPTOR_ERROR_NONE) {
        *out = adaptor.release();
    }
    return err;
}

void EngineImpl::DestroyConnector(Connector *c) {
    if (c) {
        c->Shutdown();
        delete c;
    }
}

void EngineImpl::OptionBoundaryAdjustment(struct Option *option) {
    if (option->recv_send_threads <= 0) {
        option->recv_send_threads = 1;
    }

    if (option->recv_send_threads > internal::kMaxContainerId) {
        option->recv_send_threads = internal::kMaxContainerId;
    }

    if (option->core_event_threads <= 0) {
        option->core_event_threads = 1;
    }

    if (option->init_container_size <= 0) {
        option->init_container_size = 128;
    }

    if (option->max_container_size <= 0) {
        option->max_container_size = 8192;
    }

    if (option->max_receiving_interval <= 0) {
        option->max_receiving_interval = 300000;
    }

    if (option->init_container_size / option->recv_send_threads == 0) {
        option->init_container_size = option->recv_send_threads;
    }

    if (option->max_container_size / option->recv_send_threads == 0) {
        option->max_container_size = option->recv_send_threads;
    }

    if (option->max_container_size / option->recv_send_threads >
        static_cast<int>(internal::kMaxConnectionIndex)) {
        option->max_container_size = option->recv_send_threads * internal::kMaxConnectionIndex;
    }

    auto n = option->max_container_size / option->recv_send_threads;

    option->max_container_size = option->recv_send_threads * n;

    n = option->init_container_size / option->recv_send_threads;

    option->init_container_size = option->recv_send_threads * n;

    if (option->init_container_size > option->max_container_size) {
        n = option->max_container_size;

        option->max_container_size  = option->init_container_size;
        option->init_container_size = n;
    }
}

raptor_error EngineImpl::CreateContainer(const Property &p, Container **out) {
    MessageHandler *message_handler =
        reinterpret_cast<MessageHandler *>(p.GetValue<intptr_t>(config::kMessageHandler, 0));

    if (!message_handler) {
        return RAPTOR_ERROR_FROM_STATIC_STRING("Missing MessageHandler");
    }

    *out = nullptr;

    struct Option option;
    option.msg_handler = message_handler;

    option.proto_handler =
        reinterpret_cast<ProtocolHandler *>(p.GetValue<intptr_t>(config::kProtocolHandler, 0));

    option.heartbeat_handler =
        reinterpret_cast<HeartbeatHandler *>(p.GetValue<intptr_t>(config::kHeartbeatHandler, 0));

    option.closed_handler = reinterpret_cast<EndpointClosedHandler *>(
        p.GetValue<intptr_t>(config::kEndpointClosedHandler, 0));

    option.recv_send_threads        = p.GetValue(config::kRecvSendThreads, 1);
    option.core_event_threads       = p.GetValue(config::kCoreEventThreads, 1);
    option.init_container_size      = p.GetValue(config::kInitContainerSize, 128);
    option.max_container_size       = p.GetValue(config::kMaxContainerSize, 8192);
    option.max_receiving_interval   = p.GetValue(config::kMaxReceivingInterval, 300000);
    option.check_receiving_interval = p.GetValue(config::kCheckReceivingInterval, 0);

    // adjust option
    OptionBoundaryAdjustment(&option);

    std::unique_ptr<ContainerAdaptor> adaptor(new ContainerAdaptor(&option));

#ifdef _WIN32
    raptor_error err = adaptor->Init(impl_);
#else
    raptor_error err = adaptor->Init();
#endif

    if (err == RAPTOR_ERROR_NONE) {
#ifdef _WIN32
        // adjust core event threads
        if (option.core_event_threads > 1) {
            impl_->AddThreads(option.core_event_threads - 1);
        }
#endif
        *out = adaptor.release();
    }
    return err;
}

void EngineImpl::DestroyContainer(Container *cc) {
    if (cc) {
        cc->Shutdown();
        delete cc;
    }
}

#ifdef _WIN32
EngineImpl::EngineImpl()
    : impl_(new PollingThread()) {}
EngineImpl::~EngineImpl() {
    impl_->Shutdown();
    delete impl_;
}
raptor_error EngineImpl::Init() { return impl_->Init(1); }
void EngineImpl::Start() { impl_->Start(); }
#else
EngineImpl::EngineImpl()
    : impl_(9527) {}
EngineImpl::~EngineImpl() {}
#endif

// Engine interface impl
Engine::Engine(EngineImpl *impl)
    : impl_(impl) {}

Engine::~Engine() {
    if (impl_) delete impl_;
}

raptor_error Engine::CreateAcceptor(const Property &p, Acceptor **out) {
    return impl_->CreateAcceptor(p, out);
}

void Engine::DestroyAcceptor(Acceptor *p) { impl_->DestroyAcceptor(p); }

raptor_error Engine::CreateConnector(const Property &p, Connector **out) {
    return impl_->CreateConnector(p, out);
}

void Engine::DestroyConnector(Connector *p) { impl_->DestroyConnector(p); }

raptor_error Engine::CreateContainer(const Property &p, Container **out) {
    return impl_->CreateContainer(p, out);
}

void Engine::DestroyContainer(Container *p) { impl_->DestroyContainer(p); }

} // namespace raptor

RaptorEngine RaptorCreateEngine(raptor_error *err) {
    auto impl = new raptor::EngineImpl();
#ifdef _WIN32
    auto desc = impl->Init();
    if (desc != RAPTOR_ERROR_NONE) {
        if (err) *err = desc;
        delete impl;
        return nullptr;
    }

    impl->Start();
#endif
    if (err) *err = RAPTOR_ERROR_NONE;
    return raptor::MakeRefCounted<raptor::Engine>(impl);
}
